package cn.itcast.nems.studentrecord.service.impl;

import cn.hutool.core.lang.Assert;
import cn.itcast.ic.common.exception.v2.BizExceptionProducer;
import cn.itcast.ic.common.exception.v2.bean.BizStatusEnum;
import cn.itcast.nems.base.api.ClazzApi;
import cn.itcast.nems.base.dto.ClazzDTO;
import cn.itcast.nems.common.constant.OrderStateEnum;
import cn.itcast.nems.common.enumeration.NemsErrorBodyEnum;
import cn.itcast.nems.oldems.api.StudentApi;
import cn.itcast.nems.oldems.dto.StudentDto;
import cn.itcast.nems.order.api.OrderApi;
import cn.itcast.nems.order.dto.OrderMode;
import cn.itcast.nems.order.dto.OrderModeDiscountDTO;
import cn.itcast.nems.studentrecord.constant.OrderSourceEnum;
import cn.itcast.nems.studentrecord.constant.ReferenceTypeEnum;
import cn.itcast.nems.studentrecord.converter.StudentRecordReceivableConverter;
import cn.itcast.nems.studentrecord.converter.StudentRecordReferenceConverter;
import cn.itcast.nems.studentrecord.dao.entity.StudentRecord;
import cn.itcast.nems.studentrecord.dao.entity.StudentRecordTransaction;
import cn.itcast.nems.studentrecord.dto.change.OrderCreatedDTO;
import cn.itcast.nems.studentrecord.dto.change.StudentRecordChangeDiscountDTO;
import cn.itcast.nems.studentrecord.dto.change.StudentRecordCreateDTO;
import cn.itcast.nems.studentrecord.dto.change.StudentRecordJournalDTO;
import cn.itcast.nems.studentrecord.dto.studentrecord.StudentRecordReceivableCreateDTO;
import cn.itcast.nems.studentrecord.dto.studentrecord.StudentRecordReferenceDTO;
import cn.itcast.nems.studentrecord.enumeration.StudentRecordSourceEnum;
import cn.itcast.nems.studentrecord.enumeration.StudentRecordStateEnum;
import cn.itcast.nems.studentrecord.service.studentrecord.*;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDate;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author liyong
 * create: 2023-11-22 21:04:59
 **/
@Service
public class StudentRecordSingupServiceImpl extends StudentRecordChangeServiceImpl implements StudentRecordSingupService {
    private final LocalDate DATE_3000 = LocalDate.of(2999, 12, 31);
    private final StudentApi studentApi;
    private final ClazzApi clazzApi;

    public StudentRecordSingupServiceImpl(StudentRecordTransactionService studentRecordTransactionService,
                                          StudentRecordService studentRecordService,
                                          StudentRecordCreateService studentRecordCreateService,
                                          StudentRecordMessageService studentRecordMessageService,
                                          StudentRecordReceivableService studentRecordReceivableService,
                                          ClazzApi clazzApi,
                                          StudentApi studentApi,
                                          OrderApi orderApi) {
        super(studentRecordService,
                studentRecordCreateService,
                studentRecordTransactionService,
                studentRecordMessageService,
                studentRecordReceivableService,
                orderApi);
        this.studentApi = studentApi;
        this.clazzApi = clazzApi;
    }

    @Override
    @Transactional
    public void singup(OrderCreatedDTO orderCreatedDTO) {
        logger.info("orderCreatedDTO: {}", orderCreatedDTO);
        final String source = orderCreatedDTO.getSource();
        if (OrderSourceEnum.NEW.name().equals(source)) {
            final OrderMode order = orderApi.findByBizOrderNo(orderCreatedDTO.getBizOrderNo());
            logger.info("bizOrderNo:{} order:{}", orderCreatedDTO.getBizOrderNo(), order);
            Assert.notNull(order, BizExceptionProducer.serverError(BizStatusEnum.INTERNAL_SERVER_ERROR.name(), "业务订单号: %s NEMS订单不存在", orderCreatedDTO.getBizOrderNo()));
            Assert.notNull(OrderStateEnum.CANCELED.name().equals(order.getState()), BizExceptionProducer.serverError("订单号: %s NEMS订单已被取消", order.getOrderNo()));
            StudentRecord studentRecord = this.studentRecordService.findStudentRecord(order.getId());
            if (studentRecord != null) {
                logger.warn("订单'{}'已经关联学籍'{}'", order.getId(), studentRecord.getId());
                return;
            }

            StudentDto student = this.studentApi.findByStudentOrderId(orderCreatedDTO.getBizOrderNo());
            Assert.notNull(student, BizExceptionProducer.serverError(BizStatusEnum.INTERNAL_SERVER_ERROR.name(), "业务订单号: %s EMS学籍不存在", orderCreatedDTO.getBizOrderNo()));
            final ClazzDTO clazz = this.clazzApi.queryByClassId(student.getClassId());
            Assert.notNull(clazz, BizExceptionProducer.produce(NemsErrorBodyEnum.DATA_EXIST, "班级ID：" + student.getClassId()));
            StudentRecordCreateDTO studentRecordCreateDTO = this.createStudentRecordCreateDTO(orderCreatedDTO, student, clazz, order);
            final List<String> orderLineIds = orderCreatedDTO.getOrderLineIds();
            studentRecordCreateDTO.setReferenceLineId(orderLineIds.get(0));
            studentRecordCreateDTO.setRequiredNew(false);
            logger.info("【生成学籍】封装好的学籍信息：{}", studentRecordCreateDTO);
            studentRecord = super.studentRecordCreateService.createStudentRecord(studentRecordCreateDTO);
            final StudentRecordReceivableCreateDTO dto = StudentRecordReceivableConverter.createDTO(studentRecord.getId(), orderCreatedDTO);
            super.addStudentRecordReceivable(dto);
        }
    }

    @Override
    @Transactional
    public void singupComplete(StudentRecordJournalDTO studentRecordJournal) {
        StudentRecord sr = this.studentRecordService.getById(studentRecordJournal.getTargetStudentRecordId());
        super.studentRecordChangeComplete(sr, studentRecordJournal.getTargetClazzInDate());
        StudentRecordReferenceDTO dto = StudentRecordReferenceConverter.target(sr, studentRecordJournal);
        dto.setReferenceType(ReferenceTypeEnum.ORDER);
        dto.setReferenceId(studentRecordJournal.getOrderId());
        dto.setReferenceLineId(studentRecordJournal.getOrderLineId());
        super.createStudentRecordTransaction(dto, null, StudentRecordTransaction.TYPE_IN);
    }

    private StudentRecordCreateDTO createStudentRecordCreateDTO(OrderCreatedDTO orderCreatedDTO,
                                                                StudentDto student,
                                                                ClazzDTO clazzDO,
                                                                OrderMode order) {
        StudentRecordCreateDTO recordCreateDTO = new StudentRecordCreateDTO();
        recordCreateDTO.setSource(StudentRecordSourceEnum.NEW);
        recordCreateDTO.setStudentId(student.getId());
        recordCreateDTO.setClazzId(student.getClassId());
        recordCreateDTO.setClazzName(clazzDO.getClassName());
        // final int tuitionFee = this.tuitionFeeApi.findTuitionFee(student.getClassId(), order.getPaymentTypeName());
        final Integer originalAmount = orderCreatedDTO.getOriginalAmount();
        recordCreateDTO.setPrice(originalAmount);
        final LocalDate theFreshDate = orderCreatedDTO.getTheFreshDate();
        if (theFreshDate != null) {
            recordCreateDTO.setInDate(theFreshDate.atStartOfDay());
        } else {
            if (DATE_3000.isBefore(clazzDO.getStartDate())) {
                recordCreateDTO.setInDate(orderCreatedDTO.getOrderCreatedDatetime());
            } else {
                recordCreateDTO.setInDate(clazzDO.getStartDate().atStartOfDay());
            }
        }
        final Boolean effectImmediately = orderCreatedDTO.getEffectImmediately();
        if (effectImmediately != null && effectImmediately) {
            recordCreateDTO.setState(StudentRecordStateEnum.VALID);
        }
        recordCreateDTO.setOrderId(order.getId());
        final List<OrderModeDiscountDTO> discounts = orderApi.findOrderDiscounts(order.getId());
        if (discounts != null && !discounts.isEmpty()) {
            final List<StudentRecordChangeDiscountDTO> ds = discounts
                    .stream()
                    .map(d -> new StudentRecordChangeDiscountDTO(d.getDiscountId(), d.getDiscountAmount(), d.getSort()))
                    .collect(Collectors.toList());
            recordCreateDTO.setDiscounts(ds);
        }
        recordCreateDTO.setAmountDue(orderCreatedDTO.getAmountDue());
        recordCreateDTO.setActualAmountDue(orderCreatedDTO.getAmountDue());
        recordCreateDTO.setDiscountAmount(orderCreatedDTO.getDiscountAmount());
        recordCreateDTO.setReferenceType(ReferenceTypeEnum.ORDER);
        recordCreateDTO.setReferenceId(order.getId());
        recordCreateDTO.setJoinClassId(orderCreatedDTO.getJoinClassId());
        return recordCreateDTO;
    }
}
